package generation;

public class PasswordStrength {

	//return a integer between 0 and +infinite, which correspond to the strength of the password
	public static int getPasswordStrength(char[] password) {

		//Multipliers for the final score
		int nMultMidChar=2, nMultRequirements=2, nMultConsecAlphaUC=2, nMultConsecAlphaLC=2, nMultConsecNumber=2;
		int nReqCharType=3, nMultAlphaUC=3, nMultAlphaLC=3, nMultSeqAlpha=3, nMultSeqNumber=3, nMultSeqSymbol=3;
		int nMultLength=4, nMultNumber=4;
		int nMultSymbol=6;

		int nScore = password.length * nMultLength;

		int nLength = password.length; // Longueur du password
		int nConsecAlphaUC = 0; // Nombre consecutif de MAJ
		int nConsecAlphaLC = 0; // Nombre consecutif de MIN
		int nConsecNumber = 0; // Nombre consecutif de chiffres
		int nConsecSymbol = 0; // Nombre consecutif de symboles
		int nConsecCharType = 0; // Nombre consecutif de caracteres
		int nAlphaUC = 0; // Nombre de MAJ 
		int nAlphaLC = 0; // Nombre de MIN
		int nNumber = 0; // Nombre de chiffres
		int nSymbol = 0; // Nombre de symboles
		int nMidChar = 0; // Nombre de symboles/chiffres au milieu du password

		int nRepInc = 0;
		int nRepChar = 0; 
		int nUnqChar;

		int nAlphasOnly; // only letter
		int nNumbersOnly; // only number	

		// Nombres temporaires pour calculer le nombre consecutif d'un certain type de caractère
		int nTmpAlphaUC = 0; 
		int nTmpAlphaLC = 0;
		int nTmpNumber = 0;
		int nTmpSymbol = 0;

		// Chaînes pour les séquences de caractères
		String sAlphas = "abcdefghijklmnopqrstuvwxyz";
		String sNumerics = "01234567890";
		String sSymbols = ")!@#$%^&*()";

		int nSeqAlpha = 0; // Nombre de lettres en séquences
		int nSeqNumber = 0; // Nombre de chiffre en séquences
		int nSeqSymbol = 0; // Nombre de symboles en séquences
		int nSeqChar = 0; // Nombre de caractères en séquences

		/* Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches */
		for (int a=0; a < password.length; a++) {
			Object[] arrPwd;
			if (Character.toString(password[a]).matches("[A-Z]")) {
				if (nTmpAlphaUC != ' ') { 
					if ((nTmpAlphaUC + 1) == a) { 
						nConsecAlphaUC++; 
						nConsecCharType++; 
					} 
				}
				nTmpAlphaUC = a;
				nAlphaUC++;
			}
			else if (Character.toString(password[a]).matches("[a-z]")) { 
				if (nTmpAlphaLC != ' ') { 
					if ((nTmpAlphaLC + 1) == a) { 
						nConsecAlphaLC++; nConsecCharType++; 
					} 
				}

				nTmpAlphaLC = a;
				nAlphaLC++;
			}
			else if (Character.toString(password[a]).matches("[0-9]")) { 
				if (a > 0 && a < (password.length - 1)) { nMidChar++; }
				if (nTmpNumber != ' ') { 
					if ((nTmpNumber + 1) == a) { 
						nConsecNumber++; nConsecCharType++; 
					}
				}
				nTmpNumber = a;
				nNumber++;
			}
			else if (Character.toString(password[a]).matches("[^a-zA-Z0-9_]")) { 
				if (a > 0 && a < (password.length - 1)) { nMidChar++; }
				if (nTmpSymbol != ' ') { 
					if ((nTmpSymbol + 1) == a) {
						nConsecSymbol++; nConsecCharType++;
					}
				}	
				nTmpSymbol = a;
				nSymbol++;
			}
			/* Internal loop through password to check for repeat characters */
			boolean bCharExists = false;
			for (int b=0; b < password.length; b++) {
				if (password[a] == password[b] && a != b) { /* repeat character exists */
					bCharExists = true;
					/* 
							Calculate increment deduction based on proximity to identical characters
							Deduction is incremented each time a new match is discovered
							Deduction amount is based on total password length divided by the
							difference of distance between currently selected match
					 */
					nRepInc += Math.abs(password.length/(b-a));
				}
			}
			if (bCharExists) { 
				nRepChar++; 
				nUnqChar = password.length - nRepChar;
				nRepInc = (int) ((nUnqChar > 0) ? Math.ceil(nRepInc/nUnqChar) : Math.ceil(nRepInc)); 
			}

			String tmpPwd = new String(password);
			/* Check for sequential alpha string patterns (forward and reverse) */
			for (int s=0; s < 23; s++) {
				String sFwd = sAlphas.substring(s,s+3);
				String sRev = new StringBuffer(sFwd).reverse().toString();
				if (tmpPwd.toLowerCase().indexOf(sFwd) != -1 || tmpPwd.toLowerCase().indexOf(sRev) != -1) { 
					nSeqAlpha++; nSeqChar++;
				}
			}

			/* Check for sequential numeric string patterns (forward and reverse) */
			for (int s=0; s < 8; s++) {
				String sFwd = sNumerics.substring(s,s+3);
				String sRev = new StringBuffer(sFwd).reverse().toString();
				if (tmpPwd.toLowerCase().indexOf(sFwd) != -1 || tmpPwd.toLowerCase().indexOf(sRev) != -1) { 
					nSeqNumber++; nSeqChar++;
				}
			}

			/* Check for sequential symbol string patterns (forward and reverse) */
			for (int s=0; s < 8; s++) {
				String sFwd = sSymbols.substring(s,s+3);
				String sRev = new StringBuffer(sFwd).reverse().toString();
				if (tmpPwd.toLowerCase().indexOf(sFwd) != -1 || tmpPwd.toLowerCase().indexOf(sRev) != -1) {
					nSeqSymbol++; nSeqChar++;
				}
			}

		}

		// On met à jour notre score en fonction des + et - :

		// Additions :
		if (nAlphaUC > 0 && nAlphaUC < nLength) {	
			nScore = nScore + ((nLength - nAlphaUC) * 2);
			//sAlphaUC = "+ " + parseInt((nLength - nAlphaUC) * 2); 
		}
		if (nAlphaLC > 0 && nAlphaLC < nLength) {	
			nScore = nScore + ((nLength - nAlphaLC) * 2); 
			//sAlphaLC = "+ " + parseInt((nLength - nAlphaLC) * 2);
		}
		if (nNumber > 0 && nNumber < nLength) {	
			nScore = nScore + (nNumber * nMultNumber);
			//sNumber = "+ " + parseInt(nNumber * nMultNumber);
		}
		if (nSymbol > 0) {	
			nScore = nScore + (nSymbol * nMultSymbol);
			//sSymbol = "+ " + parseInt(nSymbol * nMultSymbol);
		}
		if (nMidChar > 0) {	
			nScore = nScore + (nMidChar * nMultMidChar);
			//sMidChar = "+ " + parseInt(nMidChar * nMultMidChar);
		}

		// Soustractions :
		if ((nAlphaLC > 0 || nAlphaUC > 0) && nSymbol == 0 && nNumber == 0) {  // Only Letters
			nScore = nScore - nLength;
			nAlphasOnly = nLength;
			//sAlphasOnly = "- " + nLength;
		}
		if (nAlphaLC == 0 && nAlphaUC == 0 && nSymbol == 0 && nNumber > 0) {  // Only Numbers
			nScore = nScore - nLength; 
			nNumbersOnly = nLength;
			//sNumbersOnly = "- " + nLength;
		}
		if (nRepChar > 0) {  // Same character exists more than once
			nScore = nScore - nRepInc;
			//sRepChar = "- " + nRepInc;
		}
		if (nConsecAlphaUC > 0) {  // Consecutive Uppercase Letters exist
			nScore = nScore - (nConsecAlphaUC * nMultConsecAlphaUC); 
			//sConsecAlphaUC = "- " + parseInt(nConsecAlphaUC * nMultConsecAlphaUC);
		}
		if (nConsecAlphaLC > 0) {  // Consecutive Lowercase Letters exist
			nScore = nScore - (nConsecAlphaLC * nMultConsecAlphaLC); 
			//sConsecAlphaLC = "- " + parseInt(nConsecAlphaLC * nMultConsecAlphaLC);
		}
		if (nConsecNumber > 0) {  // Consecutive Numbers exist
			nScore = nScore - (nConsecNumber * nMultConsecNumber);  
			//sConsecNumber = "- " + parseInt(nConsecNumber * nMultConsecNumber);
		}
		if (nSeqAlpha > 0) {  // Sequential alpha strings exist (3 characters or more)
			nScore = nScore - (nSeqAlpha * nMultSeqAlpha); 
			//sSeqAlpha = "- " + parseInt(nSeqAlpha * nMultSeqAlpha);
		}
		if (nSeqNumber > 0) {  // Sequential numeric strings exist (3 characters or more)
			nScore = nScore - (nSeqNumber * nMultSeqNumber); 
			//sSeqNumber = "- " + parseInt(nSeqNumber * nMultSeqNumber);
		}
		if (nSeqSymbol > 0) {  // Sequential symbol strings exist (3 characters or more)
			nScore = nScore - (nSeqSymbol * nMultSeqSymbol); 
			//sSeqSymbol = "- " + parseInt(nSeqSymbol * nMultSeqSymbol);
		}
		if(nScore < 0)
			nScore = 0;

		return nScore;
	}

}
